<?
class DB
{
/*  Usage of place-holders syntax in all "$sql"-based functions:
		
		DB::query("select * from news where id=? and title=?", 
								10, "Hello world");

		DB::query("select * from news where id=?1 and title=?2", 
								10, "Hello world");
		
		DB::query("select * from news where id=?id and title=?title", 
								10, "Hello world");
		
		DB::query("select * from news where id=? and title=?", 
								Array(10, "Hello world") );
		
		DB::query("select * from news where id=? and title=?", 
								Array("id"=>10, "title"=>"Hello world") );
		
		DB::query("select * from news where id=?id and title=?title", 
								Array("id"=>10, "title"=>"Hello world") );
		
		DB::query("select * from news where id=?id and title=?title", 
								Array("title"=>"Hello world", "id"=>10) );
		
		//	?i for numeric, ?a for arrays, ? for strings, ?l for like %str%
		DB::query("select * from news where id=?i and title=?", 
								10, "Hello world");
	*/
	
	
	static $affected_rows;
	
	static function connect($host, $user, $password, $database, $collation='CP1251')
	{
		$res = mysql_connect($host, $user, $password);
		if($res) $res = mysql_select_db($database);
		if(!$res)
		{
			self::logConnectionError();
			throw new ExceptionNotAvailable();
		}
		mysql_query("SET NAMES ".$collation);
	}
	
	
	static function query($sql)
	{
		if(func_num_args()>1)	$sql = self::prepare($args = func_get_args());
		
		$st = microtime(true);
		$result = mysql_query($sql);
		self::$affected_rows = mysql_affected_rows();
		if(mysql_error()) DB::error($sql);
		
		// надо не лог вести а счетчики
		// extravert
		if (isset($_GET['debug']) && $_GET['debug']) $GLOBALS['queries'][] = Array("time"=>(microtime(true)-$st), "sql"=>$sql);
		return $result;
	}
	
	
	static function getAffectedRows()
	{
		return self::$affected_rows;
	}
	
	
	static function getInsertId()
	{
		return mysql_insert_id();
	}
	
	
	static function delete($table, $condition)
	{
		DB::query("DELETE FROM $table WHERE $condition");
		//Cache::clear($table,-1);
	}
	
	
	static function clearTableCache($table)
	{
		//Cache::clear($table,-1);
	}
	
	
	static function singleRow($sql)
	{
		if(func_num_args()>1)	$sql = self::prepare($args = func_get_args());
		
		$res = DB::query($sql);
		if($row = mysql_fetch_assoc($res))
		{
			return $row;
		}
		return false;
	}
	
	
	static function scalarSelect($sql)
	{
		if(func_num_args()>1)	$sql = self::prepare($args = func_get_args());
		
		$res = DB::query($sql);
		if($row = mysql_fetch_row($res))
		{
			return $row[0];
		}
		return false;
	}
	
	
	static function getRows($sql)
	{
		if(func_num_args()>1)	$sql = self::prepare($args = func_get_args());
		
		$ret=Array();
		$res = DB::query($sql);
		while($row = mysql_fetch_assoc($res))
		{
			$ret[] = $row;
		}
		mysql_free_result($res);
		return $ret;
	}
	
	
	static function hashedSelect($sql)
	{
		if(func_num_args()>1)	$sql = self::prepare($args = func_get_args());
		
		$ret=Array();
		$res = DB::query($sql);
		while($row = mysql_fetch_row($res))
		{
			$ret[$row[0]] = $row[1];
		}
		mysql_free_result($res);
		return $ret;
	}
	
	
	static function hashedRows($sql)
	{
		if(func_num_args()>1)	$sql = self::prepare($args = func_get_args());
		
		$ret=Array();
		$res = DB::query($sql);
		while($row = mysql_fetch_assoc($res))
		{
			$first = current($row);
			$ret[$first] = $row;
		}
		mysql_free_result($res);
		return $ret;
	}
	
	function getArray($sql)
	{
		if(func_num_args()>1)	$sql = self::prepare($args = func_get_args());
		
		$ret=Array();
		$res = DB::query($sql);
		while($row = mysql_fetch_array($res))
		{
			$ret[] = $row[0];
		}
		mysql_free_result($res);
		return $ret;
	}
	
	
	static function insert($table, $values=array())
	{
		$fields=array_keys($values);
		$fields_str = implode(',', $fields);
		$values_str ="'".implode("', '", self::escape($values) )."'";

        $sql = "insert into $table ($fields_str) values ($values_str)";

		DB::query($sql);
		
		//Cache::clear($table,0);
		return mysql_insert_id();
	}
	
	
	static function update($table, $id, $values=Array())
	{
		$id = intval($id);
		unset($values['id']);
		
		$fields=array_keys($values);
		$fields_str ='';
		foreach($fields as $field)
		{
				if($fields_str) $fields_str.=',';
				$fields_str.=$field."='".self::escape($values[$field])."'";
		}
		
		$sql = "update $table set $fields_str where id={$id}";
		DB::query($sql);
		
		//Cache::clear($table,$id);
		return mysql_affected_rows();
	}
	
	
	
	static function escape($value)
	{
		if(is_array($value))
		{
			return array_map("mysql_real_escape_string", $value);
		}
		else
		{
			return mysql_real_escape_string($value);
		}
	}
	
	
	// usage: prepare($sql, $args)
	//    or: prepare($args)
	static function prepare()
	{
		if(func_num_args()==1) $args = func_get_arg(0);
		else $args = func_get_args();
		
		$sql = array_shift($args);
		$sql.=' ';
		
		$mode = 'array';
		if(is_array($args[0])) 
		{
			$mode='hash';
			$args = $args[0];
		}
		
		$i=0;
		$shift=0;
		$pos = strpos($sql, '?', $shift);
		while(is_int($pos))
		{
			// находим следующий place-holder
			$pos2 = $pos;
			$key = '';
			do
			{
				$char = substr($sql, ++$pos2, 1);
				$key.=$char;
			}
			while ( in_array($char, array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','1','2','3','4','5','6','7','8','9','0','_') ) );
			$key = substr($key,0,strlen($key)-1);
			
			// выбираем значение для подстановки
			if($mode=='hash' && $key && isset($args[$key]))
			{
				$subst = "'".mysql_real_escape_string($args[$key])."'";
			}
			else
			{
				if($mode=='hash' && ( !$key || !isset($args[$key]) ) )
				{
					$mode=='array';
					$args = array_values($args);
				}
				if($key=='i')
				{
					$subst = intval($args[$i]);
				}
				if($key=='f')
				{
					$subst = floatval($args[$i]);
				}
				if($key=='l')
				{
					$subst = "'%".mysql_real_escape_string(str_replace('%','',$args[$i]))."%'";
				}
				else 
				{
					$subst = "'".mysql_real_escape_string($args[$i])."'";
				}
			}
			
			//  выполняем подстановку
			$sql = substr($sql,0,$pos) . $subst . substr($sql,$pos2);
			
			// ищем дальше
			$i++;
			$shift = $pos + strlen($subst) + 1;
			$pos = strpos($sql, '?', $shift);
		}
		
		return $sql;
	}
	
	
	function logConnectionError()
	{
		date_default_timezone_set("Europe/Moscow");
		
		$str = "Нет соединения с сервером ".date("H:i")."\r\n";
		
		$fp = fopen(BASE_DIR.'/tmp/mysql.'.date("d.m.Y").'.log','a');
		fwrite($fp,$str);
		fclose($fp);
	}
	
	
	static function error($sql)
	{
		$info = mysql_error();
		if( is_int(strpos($info,'Lost connection')) || is_int(strpos($info,'of memory')) || is_int(strpos($info,'has gone')) )
		{
			self::logConnectionError();
			throw new ExceptionNotAvailable();
		}
		
		$str = "Ошибка при выполнении запроса: ".$sql."\r\n<br>";
		$str .= "Описание ошибки: ".$info."\r\n";
		$str .= "Адрес по которому произошла ошибка: ".$_SERVER['REQUEST_URI']."\r\n";
		$str .= "\r\n";
		
		date_default_timezone_set("Europe/Moscow");
		$fp = fopen(BASE_DIR.'/tmp/error.'.date("d.m.Y").'.log','a');
		fwrite($fp,$str);
		fclose($fp);
		
		throw new ExceptionDB($sql, $info);
	}
}


class ExceptionDB extends Exception
{
	public $sql;
	public $info;
	
	public function __construct($sql, $info)
	{
		$this->sql = $sql;
		$this->info = $info;
		parent::__construct( "Ошибка при выполнении запроса." );
  }
}
